#include <unistd.h>
#include <signal.h>

#include <libubus.h>

static struct ubus_context *ctx;
static struct blob_buf b;

enum
{
    DEVICE_NAME,
    DEVICE_FIELD,
    DEVICE_DATA,
    __DEVICE_MAX
};

struct defer_request
{
    struct ubus_request_data req;
    struct uloop_timeout timer;
    int fd;
};
static struct defer_request defer_req;

static const struct blobmsg_policy device_read_policy[] = {
    [DEVICE_NAME] = {.name = "devname", .type = BLOBMSG_TYPE_STRING},
    [DEVICE_FIELD] = {.name = "fieldname", .type = BLOBMSG_TYPE_STRING},
};

static void delay_fd_reply_callback(struct uloop_timeout *t)
{
    struct defer_request *req = container_of(t, struct defer_request, timer);
    char *data = "delay reply data string\n";
    int ret;

    fprintf(stderr, "%s: enter\n", __func__);

    ret = write(req->fd, data, strlen(data));
    fprintf(stderr, "%s: send data: %s, ret: %d\n", __func__, data, ret);

    uloop_timeout_set(&req->timer, 1000);

    fprintf(stderr, "%s: exit\n", __func__);
}

static void delay_reply_callback(struct uloop_timeout *t)
{
    struct defer_request *req = container_of(t, struct defer_request, timer);
    int fds[2];

    fprintf(stderr, "%s: enter\n", __func__);

    if (pipe(fds) == -1)
    {
        fprintf(stderr, "Failed to create pipe\n");
        return;
    }

    fprintf(stderr, "fds: %d, %d\n", fds[0], fds[1]);

    ubus_request_set_fd(ctx, &req->req, fds[0]);
    ubus_complete_deferred_request(ctx, &req->req, 0);
    req->fd = fds[1];

    req->timer.cb = delay_fd_reply_callback;
    uloop_timeout_set(&req->timer, 500);

    fprintf(stderr, "%s: exit\n", __func__);
}

static int device_read(struct ubus_context *ctx, struct ubus_object *obj,
                       struct ubus_request_data *req, const char *method,
                       struct blob_attr *msg)
{
    struct blob_attr *tb[__DEVICE_MAX];
    char *devname;
    char *fieldname;

    fprintf(stderr, "%s: enter\n", __func__);

    blobmsg_parse(device_read_policy, 2, tb, blob_data(msg), blob_len(msg));
    if (!tb[DEVICE_NAME] || !tb[DEVICE_FIELD])
        return UBUS_STATUS_INVALID_ARGUMENT;

    devname = blobmsg_get_string(tb[DEVICE_NAME]);
    fieldname = blobmsg_get_string(tb[DEVICE_FIELD]);
    if (!devname || !fieldname)
        return UBUS_STATUS_UNKNOWN_ERROR;

    fprintf(stderr, "get device '%s' field '%s' data\n", devname, fieldname);

    ubus_defer_request(ctx, req, &defer_req.req);
    defer_req.timer.cb = delay_reply_callback;
    uloop_timeout_set(&defer_req.timer, 1000);

    fprintf(stderr, "%s: exit\n", __func__);
    return 0;
}

static const struct ubus_method device_methods[] = {
    UBUS_METHOD("read", device_read, device_read_policy),
};

static struct ubus_object_type device_object_type =
    UBUS_OBJECT_TYPE("device", device_methods);

static struct ubus_object device_object = {
    .name = "device",
    .type = &device_object_type,
    .methods = device_methods,
    .n_methods = ARRAY_SIZE(device_methods),
};

static void subscribe_service_objects(void)
{
    int ret;

    ret = ubus_add_object(ctx, &device_object);
    if (ret)
        fprintf(stderr, "Failed to add device object: %s\n", ubus_strerror(ret));
}

int main(int argc, char **argv)
{
    const char *ubus_socket = NULL;

    uloop_init();
    signal(SIGPIPE, SIG_IGN);

    ctx = ubus_connect(ubus_socket);
    if (!ctx)
    {
        fprintf(stderr, "Failed to connect to ubus\n");
        return -1;
    }

    ubus_add_uloop(ctx);

    subscribe_service_objects();

    uloop_run();
    ubus_free(ctx);
    uloop_done();

    return 0;
}
